package responder

import (
	"sort"

	"github.com/bitwormhole/starter/lang"
)

type Filter interface {
	// lang.PriorityProvider (opertional)
	Handle(context HttpContext, chain FilterChain) error
}

type FilterChain interface {
	Handle(context HttpContext) error
}

////////////////////////////////////////////////////////////////////////////////

type FilterWrapper struct {
	Order int
	Inner Filter
}

func (inst *FilterWrapper) _impl_() (Filter, lang.PriorityProvider) {
	return inst, inst
}

func (inst *FilterWrapper) Handle(request Request, chain FilterChain) error {
	return inst.Inner.Handle(request, chain)
}

func (inst *FilterWrapper) Priority() int {
	return inst.Order
}

////////////////////////////////////////////////////////////////////////////////

type filterChainNode struct {
	filter Filter
	next   FilterChain
	order  int
}

func (inst *filterChainNode) Handle(request Request) error {
	return inst.filter.Handle(request, inst.next)
}

func (inst *filterChainNode) makeCopy() *filterChainNode {
	o2 := &filterChainNode{}
	o2.filter = inst.filter
	o2.next = inst.next
	o2.order = inst.order
	return o2
}

////////////////////////////////////////////////////////////////////////////////

type filterChainEnding struct {
}

func (inst *filterChainEnding) Handle(request Request) error {
	return nil
}

////////////////////////////////////////////////////////////////////////////////

type FilterChainBuilder struct {
	list    []*filterChainNode
	reverse bool
}

// private

func (inst *FilterChainBuilder) theList() []*filterChainNode {
	list := inst.list
	if list == nil {
		list = make([]*filterChainNode, 0)
		inst.list = list
	}
	return list
}

func (inst *FilterChainBuilder) makeEnding() FilterChain {
	return &filterChainEnding{}
}

func (inst *FilterChainBuilder) sort() {
	sort.Sort(inst)
}

// public

func (inst *FilterChainBuilder) AddFilter(f Filter) {
	if f == nil {
		return
	}
	node := &filterChainNode{}
	node.filter = f
	pp, ok := f.(lang.PriorityProvider)
	if ok {
		node.order = pp.Priority()
	}
	list := inst.theList()
	list = append(list, node)
	inst.list = list
}

func (inst *FilterChainBuilder) AddAll(list []Filter) {
	if list == nil {
		return
	}
	for index := range list {
		item := list[index]
		inst.AddFilter(item)
	}
}

func (inst *FilterChainBuilder) Less(i1, i2 int) bool {
	n1 := inst.list[i1]
	n2 := inst.list[i2]
	if inst.reverse {
		return n1.order > n2.order
	} else {
		return n1.order < n2.order
	}
}

func (inst *FilterChainBuilder) Len() int {
	list := inst.theList()
	return len(list)
}

func (inst *FilterChainBuilder) Swap(i1, i2 int) {
	n1 := inst.list[i1]
	n2 := inst.list[i2]
	inst.list[i1] = n2
	inst.list[i2] = n1
}

func (inst *FilterChainBuilder) Create() FilterChain {
	inst.reverse = false
	inst.sort()
	list := inst.list
	pNode := inst.makeEnding()
	for index := range list {
		node := list[index].makeCopy()
		node.next = pNode
		pNode = node
	}
	return pNode
}

////////////////////////////////////////////////////////////////////////////////
